package org.afox.util;

import java.lang.reflect.*;
import java.util.*;

public class RMethod
{
	public static Object invoke(Object target, String name, List parameters) 
	{
		return invoke(target, name, parameters.toArray());
	}

	public static Object invoke(Object target, String name, Object[] parameters) 
	{
		try
		{
			return invokeCheck(target, name, parameters);
		}
		catch(NoSuchMethodException x)
		{
			throw new RuntimeException("Cannot find method \"" + name + "\" for specified target");
		}
		catch(InvocationTargetException x)
		{
			throw new RuntimeException("Exception while invoking method \"" + name + "\". " + x.getClass().getName() +
						   ":" + x.getMessage(),x.getCause());
		}
		catch(IllegalAccessException x)
		{
			throw new RuntimeException("Access denied for method \"" + name + "\"",x.getCause());
		}
		catch(Exception x)
		{
			throw new RuntimeException(x.getClass().getName() + ":" + x.getMessage(),x.getCause());
		}
	}

	public static Object invokeCheck(Object target, String name, List parameters) throws InvocationTargetException,
											     NoSuchMethodException,
											     IllegalAccessException
	{
		return invokeCheck(target, name, parameters.toArray());
	}

	public static Object invokeCheck(Object target, String name, Object[] parameters) throws InvocationTargetException,
												 NoSuchMethodException,
												 IllegalAccessException
	{
		return getMethod(target.getClass(), name, RHelper.getTypes(parameters)).invoke(target, parameters);
	}

	private static Method getMethod(Class aClass, String name, Class[] parmTypes) throws NoSuchMethodException
	{
		return matchMethod(aClass.getMethods(), name, parmTypes);
	}

	private static Method matchMethod(Method[] methods, String name, Class[] parmTypes) throws NoSuchMethodException
	{
		for (int i = 0; i< methods.length; i++)
		{
			// If method name doesn't match, go to next method.
			if (!methods[i].getName().equals(name))
				continue;

			if (methods[i].getParameterTypes().length == 0 && parmTypes.length == 0)
				return methods[i];
			    
			// If the number of parameters don't match, go to next method.
			if (methods[i].getParameterTypes().length!=parmTypes.length)
				continue;

			if (TypeMapper.matchParameters(methods[i].getParameterTypes(), parmTypes))
				return methods[i];
		}
		throw new NoSuchMethodException("Cannot find method to match:" + parmTypes);
	}

	public static Object invokeClassMethod(String className, String methodName, List parameters)
	{
		return invokeClassMethod(className, methodName, parameters.toArray());
	}

	public static Object invokeClassMethod(String className, String methodName, Object[] parameters)
	{
		try
		{
			return invokeClassMethod(Class.forName(className), methodName, parameters);
		}
		catch(ClassNotFoundException x)
		{
			throw new RuntimeException("Cannot find class:" + className);
		}
		catch(Exception x)
		{
			throw new RuntimeException("Exception:" + x);
		}
	}

	public static Object invokeClassMethod(Class aClass, String methodName, List parameters)
	{
		return invokeClassMethod(aClass, methodName, parameters.toArray());
	}

	public static Object invokeClassMethod(Class aClass, String methodName, Object[] parameters)
	{
		try
		{
			return getMethod(aClass, methodName, RHelper.getTypes(parameters)).invoke(null, parameters);
		}
		catch(Exception x)
		{
			x.printStackTrace();
			throw new RuntimeException("Exception:" + x);
		}
	}

}
